﻿#region Using Statements
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics;
using Microsoft.Xna.Framework;
using System.Xml;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Content;
#endregion // Using Statements

namespace ParticleSystem
{
    /// <summary>
    /// Utility functions used throughout the project.
    /// </summary>
    public class Utility
    {
        #region Fields
        /// <summary>
        /// Random number generator.
        /// </summary>
        public static Random random = new Random();
        #endregion // Fields

        #region Random Number Generators
        /// <summary>
        /// Generate a random integer within a specified range.
        /// </summary>
        /// <param name="min">The minimum integer value returned.</param>
        /// <param name="max">The maximum integer value returned.</param>
        /// <returns></returns>
        public static int randomInt(int min, int max)
        {
            Debug.Assert(min <= max);
            return random.Next(min, max);
        }

        /// <summary>
        /// Generate a random float value wihtin a specified range.
        /// </summary>
        /// <param name="min">The minimum float value returned.</param>
        /// <param name="max">The maximum float value returned.</param>
        /// <returns></returns>
        public static float randomFloat(float min = 0, float max = 1)
        {
            Debug.Assert(min <= max);

            float randomFloat = (float)random.NextDouble();
            return randomFloat * (max - min) + min;
        }
        #endregion // Random Number Generators

        #region XML Parsers
        /// <summary>
        /// Create a collision behavior from an XML element.
        /// </summary>
        /// <param name="collisionBehaviorElement">The XML element containing information about the collision behavior.</param>
        /// <returns>The collision behavior created from the XML element.</returns>
        public static CollisionBehavior CollisionBehaviorFromXml(XmlElement collisionBehaviorElement)
        {
            String collisionBehaviorStr = collisionBehaviorElement.ChildNodes[0].Value;
            
            // find the collision behavior
            foreach (CollisionBehavior collisionBehavior in Enum.GetValues(typeof(CollisionBehavior)))
            {
                if(collisionBehaviorStr.Equals(collisionBehavior.ToString()))
                {
                    return collisionBehavior;
                }
            }

            // if we're here, the collision behavior does not exist
            throw new Exception("Could not parse CollisionBehavior from XML!");
        }

        /// <summary>
        /// Create a Vector3 from an XML element.
        /// </summary>
        /// <param name="vector3Element">The XML element contianing information about the vector.</param>
        /// <returns>The vector created from the XML element.</returns>
        public static Vector3 Vector3FromXml(XmlElement vector3Element)
        {
            Vector3 vector3 = Vector3.Zero;

            foreach (XmlElement vector3ChildElement in vector3Element.ChildNodes)
            {
                // found x direction component
                if (vector3ChildElement.Name.Equals("X"))
                {
                    vector3.X = float.Parse(vector3ChildElement.ChildNodes[0].Value);
                }
                // found y direction component
                else if (vector3ChildElement.Name.Equals("Y"))
                {
                    vector3.Y = float.Parse(vector3ChildElement.ChildNodes[0].Value);
                }
                // found z direction component
                else if (vector3ChildElement.Name.Equals("Z"))
                {
                    vector3.Z = float.Parse(vector3ChildElement.ChildNodes[0].Value);
                }
            }

            return vector3;
        }

        /// <summary>
        /// Create a color from an XML element.
        /// </summary>
        /// <param name="colorElement">The XML element contianing information about the color.</param>
        /// <returns>The color created from the XML element.</returns>
        public static Color ColorFromXml(XmlElement colorElement)
        {
            float r = 0.0f, g = 0.0f, b = 0.0f;

            foreach (XmlElement colorChildElement in colorElement.ChildNodes)
            {
                // found red component
                if (colorChildElement.Name.Equals("R"))
                {
                    r = float.Parse(colorChildElement.ChildNodes[0].Value);
                }
                // found green component
                else if (colorChildElement.Name.Equals("G"))
                {
                    g = float.Parse(colorChildElement.ChildNodes[0].Value);
                }
                // found blue component
                else if (colorChildElement.Name.Equals("B"))
                {
                    b = float.Parse(colorChildElement.ChildNodes[0].Value);
                }
            }

            return new Color(r/255.0f, g/255.0f, b/255.0f);
        }

        /// <summary>
        /// Create an integer range from an XML element.
        /// </summary>
        /// <param name="rangeElement">The XML element contianing information about the integer range.</param>
        /// <returns>The integer range created from the XML element.</returns>
        public static Range<int> IntRangeFromXml(XmlElement rangeElement)
        {
            Range<XmlElement> xmlElementRange = Range<XmlElement>.XmlElementRange(rangeElement);
            int min = int.Parse(xmlElementRange.min.ChildNodes[0].Value);
            int max = int.Parse(xmlElementRange.max.ChildNodes[0].Value);
            return new Range<int>(min, max);
        }

        /// <summary>
        /// Create a float range from an XML element.
        /// </summary>
        /// <param name="rangeElement">The XML element contianing information about the float range.</param>
        /// <returns>The float range created from the XML element.</returns>
        public static Range<float> FloatRangeFromXml(XmlElement rangeElement)
        {
            Range<XmlElement> xmlElementRange = Range<XmlElement>.XmlElementRange(rangeElement);
            float min = float.Parse(xmlElementRange.min.ChildNodes[0].Value);
            float max = float.Parse(xmlElementRange.max.ChildNodes[0].Value);
            return new Range<float>(min, max);
        }

        /// <summary>
        /// Create a color range from an XML element.
        /// </summary>
        /// <param name="rangeElement">The XML element contianing information about the color range.</param>
        /// <returns>The color range created from the XML element.</returns>
        public static Range<Color> ColorRangeFromXml(XmlElement rangeElement)
        {
            Range<XmlElement> xmlElementRange = Range<XmlElement>.XmlElementRange(rangeElement);
            return new Range<Color>(ColorFromXml(xmlElementRange.min), ColorFromXml(xmlElementRange.max));
        }
        #endregion // XML Parsers

        #region Model Loading
        public static Model LoadModel(ContentManager contentMgr, Effect effect, string assetName, out Texture2D[] textures)
        {
            Model newModel = contentMgr.Load<Model>(assetName);
            textures = new Texture2D[newModel.Meshes.Count];
            int i = 0;
            foreach (ModelMesh mesh in newModel.Meshes)
                foreach (BasicEffect currentEffect in mesh.Effects)
                    textures[i++] = currentEffect.Texture;

            foreach (ModelMesh mesh in newModel.Meshes)
                foreach (ModelMeshPart meshPart in mesh.MeshParts)
                    meshPart.Effect = effect.Clone();

            return newModel;
        }
        #endregion // Model Loading
    }
}
